package com.example.commonUpAndDownFileutil.controller;


import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 分片下载
 */
@RestController
@RequestMapping("/api/upload2")
public class UploadController2 {
    // 上传文件保存路径
    private static final String UPLOAD_DIR = "uploads/";
    // 分片临时保存路径
    private static final String CHUNK_DIR = UPLOAD_DIR + "chunks/";

    // 允许的文件类型
    private static final List<String> ALLOWED_CONTENT_TYPES = List.of(
            "image/jpeg", "image/png", "image/gif",
            "application/pdf", "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "text/plain",
            "application/exe", "application/x-msdownload",  // EXE文件的MIME类型
            "application/zip", "application/x-zip-compressed" , // ZIP文件的MIME类型
            "application/octet-stream" // 分片方式
    );

    // 配置：文件大小超过此值将自动分片（50MB）
    private static final long AUTO_CHUNK_THRESHOLD = 100 * 1024 * 1024;
    // 分片大小
    public static final long CHUNK_SIZE = 100 * 1024 * 1024;
    // 最大文件大小限制（1GB）
    private static final long MAX_FILE_SIZE = 1024 * 1024 * 1024;

    // 用于跟踪分片上传状态的缓存
    private final Map<String, UploadStatus> uploadStatusCache = new ConcurrentHashMap<>();

    /**
     * 智能文件上传接口
     * 小文件：直接上传
     * 大文件：自动分片上传（对用户透明）
     */
    @PostMapping("/smart")
    public ResponseEntity<?> smartUpload(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "uploadId", required = false) String uploadId,
            @RequestParam(value = "chunkIndex", required = false, defaultValue = "-1") int chunkIndex) {

        try {
            // 验证文件是否为空
            if (file.isEmpty()) {
                return ResponseEntity.badRequest().body("上传的文件不能为空");
            }

            // 验证文件类型
            if (!ALLOWED_CONTENT_TYPES.contains(file.getContentType())) {
                return ResponseEntity.badRequest().body("不支持的文件类型");
            }

            // 验证文件大小
            if (file.getSize() > MAX_FILE_SIZE) {
                return ResponseEntity.badRequest().body("文件大小不能超过1GB");
            }

            // 确保上传目录存在
            createUploadDirIfNotExists();

            // 小文件：直接上传
            if ( chunkIndex == -1 &&  file.getSize() <= AUTO_CHUNK_THRESHOLD) {
                return handleSmallFileUpload(file);
            }

            // 大文件：自动分片处理
            return handleLargeFileAutoChunkUpload(file, uploadId, chunkIndex);

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 处理小文件直接上传
     */
    private ResponseEntity<?> handleSmallFileUpload(MultipartFile file) throws IOException {
        // 生成唯一文件名，避免覆盖
        String originalFilename = file.getOriginalFilename();
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String uniqueFileName = UUID.randomUUID().toString() + fileExtension;

        // 保存文件
        Path path = Paths.get(UPLOAD_DIR + uniqueFileName);
        Files.write(path, file.getBytes());

        // 返回成功信息
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("message", "文件上传成功");
        result.put("fileName", uniqueFileName);
        result.put("originalFileName", originalFilename);
        result.put("fileSize", file.getSize());

        return ResponseEntity.ok(result);
    }

    /**
     * 处理大文件自动分片上传
     * 对用户透明，系统自动处理分片逻辑
     */
    private ResponseEntity<?> handleLargeFileAutoChunkUpload(
            MultipartFile file, String uploadId, int chunkIndex) throws IOException {

        // 第一次请求，初始化分片上传
        if (uploadId == null || uploadId.isEmpty()) {
            return initAutoChunkUpload(file);
        }

        // 后续请求，处理分片上传
        if (chunkIndex >= 0) {
            return processAutoChunkUpload(uploadId, chunkIndex, file);
        }

        return ResponseEntity.badRequest().body("无效的请求参数");
    }

    /**
     * 初始化自动分片上传
     */
    private ResponseEntity<?> initAutoChunkUpload(MultipartFile file) {
        try {
            // 创建必要的目录
            createChunkDirIfNotExists();

            // 生成上传ID
            String uploadId = UUID.randomUUID().toString();
            // 计算分片数量
            int chunkCount = (int) Math.ceil((double) file.getSize() / CHUNK_SIZE);
            // 获取文件扩展名
            String originalFilename = file.getOriginalFilename();
            String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 生成最终保存的文件名
            String uniqueFileName = UUID.randomUUID().toString() + fileExtension;

            // 记录上传状态
            UploadStatus status = new UploadStatus();
            status.setUploadId(uploadId);
            status.setFileName(uniqueFileName);
            status.setOriginalFileName(originalFilename);
            status.setContentType(file.getContentType());
            status.setFileSize(file.getSize());
            status.setChunkCount(chunkCount);
            status.setUploadedChunks(new HashSet<>());
            status.setCreateTime(new Date());

            uploadStatusCache.put(uploadId, status);

            // 构建响应
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("uploadId", uploadId);
            result.put("chunkCount", chunkCount);
            result.put("chunkSize", CHUNK_SIZE);
            result.put("message", "分片上传初始化成功，请继续上传分片");

            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("初始化分片上传失败: " + e.getMessage());
        }
    }

    /**
     * 处理分片上传和合并
     */
    private ResponseEntity<?> processAutoChunkUpload(String uploadId, int chunkIndex, MultipartFile chunkFile) throws IOException {
        // 验证上传ID是否存在
        UploadStatus status = uploadStatusCache.get(uploadId);
        if (status == null) {
            return ResponseEntity.badRequest().body("无效的uploadId");
        }

        // 验证分片索引是否有效
        if (chunkIndex < 0 || chunkIndex >= status.getChunkCount()) {
            return ResponseEntity.badRequest().body("无效的分片索引");
        }

        // 验证分片文件
        if (chunkFile.isEmpty()) {
            return ResponseEntity.badRequest().body("分片文件不能为空");
        }

        // 保存分片
        String chunkFileName = uploadId + "_" + chunkIndex;
        Path chunkPath = Paths.get(CHUNK_DIR + chunkFileName);
        Files.write(chunkPath, chunkFile.getBytes());

        // 更新已上传的分片信息
        status.getUploadedChunks().add(chunkIndex);

        // 检查是否所有分片都已上传
        if (status.getUploadedChunks().size() == status.getChunkCount()) {
            // 合并所有分片
            return mergeChunks(status);
        } else {
            // 还有分片未上传
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("uploadId", uploadId);
            result.put("chunkIndex", chunkIndex);
            result.put("completed", false);
            result.put("uploadedChunks", status.getUploadedChunks().size());
            result.put("totalChunks", status.getChunkCount());
            result.put("message", "分片上传成功，请继续上传下一分片");

            return ResponseEntity.ok(result);
        }
    }

    /**
     * 合并分片文件
     */
    private ResponseEntity<?> mergeChunks(UploadStatus status) {
        try {
            // 合并分片
            String finalFilePath = UPLOAD_DIR + status.getFileName();
            try (FileOutputStream fos = new FileOutputStream(finalFilePath)) {
                for (int i = 0; i < status.getChunkCount(); i++) {
                    String chunkFileName = status.getUploadId() + "_" + i;
                    Path chunkPath = Paths.get(CHUNK_DIR + chunkFileName);

                    // 将分片内容写入最终文件
                    byte[] chunkBytes = Files.readAllBytes(chunkPath);
                    fos.write(chunkBytes);

                    // 删除临时分片文件
                    Files.delete(chunkPath);
                }
            }

            // 从缓存中移除上传状态
            uploadStatusCache.remove(status.getUploadId());

            // 构建响应
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "文件上传成功");
            result.put("fileName", status.getFileName());
            result.put("originalFileName", status.getOriginalFileName());
            result.put("fileSize", status.getFileSize());
            result.put("completed", true);

            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("分片合并失败: " + e.getMessage());
        }
    }

    /**
     * 查询上传状态
     */
    @GetMapping("/status")
    public ResponseEntity<?> getUploadStatus(@RequestParam String uploadId) {
        UploadStatus status = uploadStatusCache.get(uploadId);
        if (status == null) {
            return ResponseEntity.badRequest().body("无效的uploadId");
        }

        Map<String, Object> result = new HashMap<>();
        result.put("uploadId", uploadId);
        result.put("fileName", status.getOriginalFileName());
        result.put("fileSize", status.getFileSize());
        result.put("chunkCount", status.getChunkCount());
        result.put("uploadedChunks", status.getUploadedChunks().size());
        result.put("completed", status.getUploadedChunks().size() == status.getChunkCount());

        return ResponseEntity.ok(result);
    }

    // 创建上传目录（如果不存在）
    private void createUploadDirIfNotExists() {
        File uploadDir = new File(UPLOAD_DIR);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
    }

    // 创建分片目录（如果不存在）
    private void createChunkDirIfNotExists() {
        File chunkDir = new File(CHUNK_DIR);
        if (!chunkDir.exists()) {
            chunkDir.mkdirs();
        }
    }

    // 内部类，用于跟踪分片上传状态
    private static class UploadStatus {
        private String uploadId;
        private String fileName;
        private String originalFileName;
        private String contentType;
        private long fileSize;
        private int chunkCount;
        private Set<Integer> uploadedChunks;
        private Date createTime;

        // Getters and Setters
        public String getUploadId() {
            return uploadId;
        }

        public void setUploadId(String uploadId) {
            this.uploadId = uploadId;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public String getOriginalFileName() {
            return originalFileName;
        }

        public void setOriginalFileName(String originalFileName) {
            this.originalFileName = originalFileName;
        }

        public String getContentType() {
            return contentType;
        }

        public void setContentType(String contentType) {
            this.contentType = contentType;
        }

        public long getFileSize() {
            return fileSize;
        }

        public void setFileSize(long fileSize) {
            this.fileSize = fileSize;
        }

        public int getChunkCount() {
            return chunkCount;
        }

        public void setChunkCount(int chunkCount) {
            this.chunkCount = chunkCount;
        }

        public Set<Integer> getUploadedChunks() {
            return uploadedChunks;
        }

        public void setUploadedChunks(Set<Integer> uploadedChunks) {
            this.uploadedChunks = uploadedChunks;
        }

        public Date getCreateTime() {
            return createTime;
        }

        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    }
}

